#------------------------------------------------------------------------------
# Setup cmake module
#------------------------------------------------------------------------------
set(MERCURY_UTIL_CMAKE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/CMake")
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${MERCURY_UTIL_CMAKE_DIR})

#------------------------------------------------------------------------------
# Include source and build directories
#------------------------------------------------------------------------------
set(MERCURY_UTIL_BUILD_INCLUDE_DEPENDENCIES
  ${CMAKE_CURRENT_SOURCE_DIR}
  ${CMAKE_CURRENT_BINARY_DIR}
)

#------------------------------------------------------------------------------
# External dependencies
#------------------------------------------------------------------------------
include(CheckIncludeFiles)
include(CheckSymbolExists)
include(CheckTypeSize)

# Threads
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
find_package(Threads REQUIRED)

set(MERCURY_UTIL_EXT_LIB_DEPENDENCIES
  ${MERCURY_UTIL_EXT_LIB_DEPENDENCIES}
  ${CMAKE_THREAD_LIBS_INIT}
)
if(CMAKE_USE_PTHREADS_INIT)
  set(CMAKE_EXTRA_INCLUDE_FILES pthread.h)
  set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_THREAD_LIBS_INIT})

  # Detect pthread_spinlock_t
  check_type_size(pthread_spinlock_t HG_UTIL_HAS_PTHREAD_SPINLOCK_T)

  # Use type size to check enum value
  check_type_size(PTHREAD_MUTEX_ADAPTIVE_NP HG_UTIL_HAS_PTHREAD_MUTEX_ADAPTIVE_NP)

  # Detect pthread_condattr_setclock
  check_symbol_exists(pthread_condattr_setclock pthread.h
    HG_UTIL_HAS_PTHREAD_CONDATTR_SETCLOCK)

  unset(CMAKE_EXTRA_INCLUDE_FILES)
  unset(CMAKE_REQUIRED_LIBRARIES)
endif()

# Rt
if(NOT WIN32 AND NOT APPLE)
  set(MERCURY_UTIL_EXT_LIB_DEPENDENCIES
    ${MERCURY_UTIL_EXT_LIB_DEPENDENCIES}
    -lrt
  )
endif()

# Monotonic time
option(MERCURY_USE_MONOTONIC_CLOCK "Use monotonic clock for time." OFF)
if(MERCURY_USE_MONOTONIC_CLOCK)
  set(HG_UTIL_HAS_CLOCK_MONOTONIC 1)
endif()
mark_as_advanced(MERCURY_USE_MONOTONIC_CLOCK)

# Detect <time.h>
check_include_files("time.h" HG_UTIL_HAS_TIME_H)
if(HG_UTIL_HAS_TIME_H)
  # Detect clock_gettime
  check_symbol_exists(clock_gettime time.h HG_UTIL_HAS_CLOCK_GETTIME)
endif()

# Detect <sys/time.h>
check_include_files("sys/time.h" HG_UTIL_HAS_SYSTIME_H)

# Detect <sys/epoll.h>
check_include_files("sys/epoll.h" HG_UTIL_HAS_SYSEPOLL_H)

# Detect <sys/eventfd.h>
check_include_files("sys/eventfd.h" HG_UTIL_HAS_SYSEVENTFD_H)
if(HG_UTIL_HAS_SYSEVENTFD_H)
  set(CMAKE_EXTRA_INCLUDE_FILES "sys/eventfd.h")
  check_type_size(eventfd_t HG_UTIL_HAS_EVENTFD_T)
endif()

# Detect <sys/event.h>
check_include_files("sys/event.h" HG_UTIL_HAS_SYSEVENT_H)

# Atomics
if(NOT WIN32)
  # Detect stdatomic
  check_include_files("stdatomic.h" HG_UTIL_HAS_STDATOMIC_H)
  # Detect size of atomic_long
  set(CMAKE_EXTRA_INCLUDE_FILES stdatomic.h)
  check_type_size(atomic_long HG_UTIL_ATOMIC_LONG_WIDTH)
  unset(CMAKE_EXTRA_INCLUDE_FILES)
  # OpenPA
  option(MERCURY_USE_OPA "Use OpenPA for atomics." OFF)
  # Force use of OPA if <stdatomic.h> is not found
  if(NOT HG_UTIL_HAS_STDATOMIC_H)
    set(MERCURY_USE_OPA "ON" CACHE BOOL "Use OpenPA for atomics." FORCE)
  endif()
  mark_as_advanced(MERCURY_USE_OPA)
  if(MERCURY_USE_OPA)
    # Use OpenPA if stdatomic is not available
    find_package(OPA REQUIRED)
    message(STATUS "OPA include directory: ${OPA_INCLUDE_DIRS}")
    set(HG_UTIL_HAS_OPA_PRIMITIVES_H 1)
    set(MERCURY_UTIL_EXT_INCLUDE_DEPENDENCIES
      ${MERCURY_UTIL_EXT_INCLUDE_DEPENDENCIES}
      ${OPA_INCLUDE_DIRS}
    )
  endif()
endif()

# Colored output
option(MERCURY_ENABLE_LOG_COLOR "Use colored output for log." OFF)
if(MERCURY_ENABLE_LOG_COLOR)
  set(HG_UTIL_HAS_LOG_COLOR 1)
endif()
mark_as_advanced(MERCURY_ENABLE_LOG_COLOR)

#------------------------------------------------------------------------------
# Configure module header files
#------------------------------------------------------------------------------
# Set unique var used in the autogenerated config file (symbol import/export)
if(BUILD_SHARED_LIBS)
  set(HG_UTIL_BUILD_SHARED_LIBS 1)
  set(MERCURY_UTIL_LIBTYPE SHARED)
else()
  set(HG_UTIL_BUILD_SHARED_LIBS 0)
  set(MERCURY_UTIL_LIBTYPE STATIC)
endif()

if(MERCURY_ENABLE_VERBOSE_ERROR)
  set(HG_UTIL_HAS_VERBOSE_ERROR 1)
else()
  set(HG_UTIL_HAS_VERBOSE_ERROR 0)
endif()

configure_file(
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_util_config.h.in
  ${CMAKE_CURRENT_BINARY_DIR}/mercury_util_config.h
)

#------------------------------------------------------------------------------
# Set sources
#------------------------------------------------------------------------------
set(MERCURY_UTIL_SRCS
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_atomic_queue.c
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_event.c
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_hash_table.c
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_log.c
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_mem.c
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_poll.c
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_request.c
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_thread.c
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_thread_condition.c
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_thread_mutex.c
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_thread_pool.c
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_thread_rwlock.c
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_thread_spin.c
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_util_error.c
)

#----------------------------------------------------------------------------
# Libraries
#----------------------------------------------------------------------------

# Clean up system include path first
foreach(item ${MERCURY_SYSTEM_INCLUDE_PATH})
  if(MERCURY_UTIL_EXT_INCLUDE_DEPENDENCIES)
    list(REMOVE_ITEM MERCURY_UTIL_EXT_INCLUDE_DEPENDENCIES ${item})
  endif()
endforeach()

# UTIL
add_library(mercury_util ${MERCURY_UTIL_SRCS})
if(THREADS_HAVE_PTHREAD_ARG)
  target_compile_options(mercury_util PUBLIC "${CMAKE_THREAD_LIBS_INIT}")
endif()
target_include_directories(mercury_util
  PUBLIC "$<BUILD_INTERFACE:${MERCURY_UTIL_BUILD_INCLUDE_DEPENDENCIES}>"
          $<INSTALL_INTERFACE:${MERCURY_INSTALL_INCLUDE_INTERFACE}>
)
target_include_directories(mercury_util
  SYSTEM PUBLIC ${MERCURY_UTIL_EXT_INCLUDE_DEPENDENCIES}
)
target_link_libraries(mercury_util ${MERCURY_UTIL_EXT_LIB_DEPENDENCIES})
mercury_set_lib_options(mercury_util "mercury_util" ${MERCURY_UTIL_LIBTYPE})
if(MERCURY_ENABLE_COVERAGE)
  set_coverage_flags(mercury_util)
endif()

#---------------------------------------------------------------------------
# Add Target(s) to CMake Install
#---------------------------------------------------------------------------
install(
  TARGETS
    mercury_util
  EXPORT
    ${MERCURY_EXPORTED_TARGETS}
  LIBRARY DESTINATION ${MERCURY_INSTALL_LIB_DIR}
  ARCHIVE DESTINATION ${MERCURY_INSTALL_LIB_DIR}
  RUNTIME DESTINATION ${MERCURY_INSTALL_BIN_DIR}
)

#-----------------------------------------------------------------------------
# Specify project header files to be installed
#-----------------------------------------------------------------------------
set(MERCURY_HEADERS
  ${CMAKE_CURRENT_BINARY_DIR}/mercury_util_config.h
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_atomic.h
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_atomic_queue.h
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_event.h
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_hash_string.h
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_hash_table.h
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_list.h
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_log.h
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_mem.h
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_poll.h
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_queue.h
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_request.h
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_thread.h
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_thread_condition.h
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_thread_mutex.h
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_thread_pool.h
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_thread_rwlock.h
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_thread_spin.h
  ${CMAKE_CURRENT_SOURCE_DIR}/mercury_time.h
)

#-----------------------------------------------------------------------------
# Add file(s) to CMake Install
#-----------------------------------------------------------------------------
install(
  FILES
    ${MERCURY_HEADERS}
  DESTINATION
    ${MERCURY_INSTALL_INCLUDE_DIR}
  COMPONENT
    headers
)

#------------------------------------------------------------------------------
# Set variables for parent scope
#------------------------------------------------------------------------------
set(MERCURY_UTIL_EXT_INCLUDE_DEPENDENCIES ${MERCURY_UTIL_EXT_INCLUDE_DEPENDENCIES} PARENT_SCOPE)
set(MERCURY_UTIL_EXT_LIB_DEPENDENCIES ${MERCURY_UTIL_EXT_LIB_DEPENDENCIES} PARENT_SCOPE)
